/* vim: set ts=2 et sw=2 cindent fo=qroca: */

package com.globant.google.mendoza;

import java.util.List;
import java.util.Date;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;

import javax.xml.datatype.XMLGregorianCalendar;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.globant.google.mendoza.malbec.schema._2.NewOrderNotification;
import com.globant.google.mendoza.malbec.schema._2.RiskInformationNotification;

import com.globant.google.mendoza.command.MendozaCommand;

/** Represents the server status which contains the merchant calculations,
 * the new order notification, the risk information and the order state
 * changes.
 * It also stores a log for the commands that were issued to the server.
 */
public final class MendozaStatus {

  /** The class logger.
   */
  private static Log log = LogFactory.getLog(MendozaStatus.class);

  /** List of the received commands (login, placeOrder, etc).
   */
  private List<MendozaCommand> commands = new ArrayList<MendozaCommand>();

  /** The merchant calculations. */
  private List<MendozaMerchantCalculation> merchantCalculations =
    new ArrayList<MendozaMerchantCalculation>();

  /** The new order notification. */
  private NewOrderNotification newOrderNotification;

  /** The risk information notification. */
  private RiskInformationNotification riskInformation;

  /** The new order notification message. */
  private String newOrderNotificationMessage = "";

  /** The risk information notification message. */
  private String riskInformationMessage = "";

  /** The order fulfillment states. */
  private Map<Long, String> fulfillmentStates =
    new TreeMap<Long, String>();

  /** The order financial states. */
  private Map<Long, String> financialStates =
    new TreeMap<Long, String>();

  /** The fulfillment current state. */
  private String fulfillmentCurrentState = "";

  /** The financial current state. */
  private String financialCurrentState = "";

  /** The google order number.
   */
  private String googleOrderNumber = "";

  /** Gets the issued commands to the server.
   *
   * @return Returns the issued commands.
   */
  public List<MendozaCommand> getCommands() {
    return commands;
  }

  /** Adds a command that was issued to the Mendoza Server.
   *
   * @param command The command.
   */
  public void addCommand(final MendozaCommand command) {
    commands.add(command);
  }

  /** Adds a merchant calculation.
   *
   *  @param merchantCalculation The merchant calculation to add.
   */
  public void addMerchantCalculation(
          final MendozaMerchantCalculation merchantCalculation) {
    merchantCalculations.add(merchantCalculation);
  }

  /** Gets the merchant calculations.
   *
   * @return Returns the merchant calculations.
   */
  public List<MendozaMerchantCalculation> getMerchantCalculations() {
    return merchantCalculations;
  }

  /** Gets the new order notification message received from checkout.
   *
   * @return Returns the new order notification message received.
   */
  public String getNewOrderNotificationMsg() {
    return newOrderNotificationMessage;
  }

  /** Gets the new order notification received from checkout.
   *
   * @return Returns the new order notification received.
   */
  public NewOrderNotification getNewOrderNotification() {
    return newOrderNotification;
  }

  /** Sets the newOrderNotification message received from checkout.
   *
   * @param newOrderNotificationMsg The new  order notification message
   * received.
   *
   * @param theNewOrderNotification The new order notification received.
   */
  public void setNewOrderNotification(final String newOrderNotificationMsg,
      final NewOrderNotification theNewOrderNotification) {
    newOrderNotificationMessage = newOrderNotificationMsg;
    newOrderNotification = theNewOrderNotification;
  }

  /** Gets the new order notification timestamp.
   *
   * @return Returns the new order notification timestamp. If the New Order
   * Notification Timestamp is null returns -1.
   */
  public long getNewOrderNotificationTimestamp() {
    log.trace("Entering getNewOrderNotificationTimestamp");
    long result = -1;
    XMLGregorianCalendar xmlCalendar =
      getNewOrderNotification().getTimestamp();
    if (xmlCalendar != null) {
      result = getTimeInMillis(xmlCalendar);
    }
    log.trace("Leaving getNewOrderNotificationTimestamp");
    return result;
  }

  /** Sets the google order number.
   *
   * @param orderNumber The google order number.
   */
  public void setGoogleOrderNumber(final String orderNumber) {
    googleOrderNumber = orderNumber;
  }

  /** Gets the google order number.
   *
   * @return Returns the google order number.
   */
  public String getGoogleOrderNumber() {
    return googleOrderNumber;
  }

  /** Gets the risk information notification timestamp.
   *
   * @return Returns the risk informatino notification timestamp.
   */
  public long getRiskInformationTimestamp() {
    log.trace("Entering getRiskInformationTimestamp");
    long result = -1;
    XMLGregorianCalendar xmlCalendar =
      getRiskInformationNotification().getTimestamp();
    if (xmlCalendar != null) {
      result = getTimeInMillis(xmlCalendar);
    }
    log.trace("Leaving getRiskInformationTimestamp");
    return result;
  }

  /** Gets the time in millis for an xml calendar.
   *
   * @param xmlCalendar The xml calendar.
   *
   * @return Returns the time in millis.
   */
  private long getTimeInMillis(final XMLGregorianCalendar xmlCalendar) {
    return xmlCalendar.toGregorianCalendar().getTimeInMillis();
  }

  /** Sets the Risk Information Notification.
   *
   * @param riskInformationMsg The Risk Information Notification message.
   *
   * @param theRiskInformation The Risk Information Notification.
   */
  public void setRiskInformationNotification(final String riskInformationMsg,
      final RiskInformationNotification theRiskInformation) {
    riskInformationMessage = riskInformationMsg;
    riskInformation = theRiskInformation;
  }

  /** Gets the risk information notification message.
   *
   * @return Returns the risk information notification message.
   */
  public String getRiskInformationNotificationMsg() {
    return riskInformationMessage;
  }

  /** Gets the risk information notification.
   *
   * @return Returns the risk information notification.
   */
  public RiskInformationNotification getRiskInformationNotification() {
    return riskInformation;
  }

  /** Gets the order fulfillment states.
   *
   * @return Returns the order fulfillment states.
   */
  public Map<Long, String> getFulfillmentStates() {
    return fulfillmentStates;
  }

  /** Gets the order financial states.
   *
   * @return Returns the order financial states.
   */
  public Map<Long, String> getFinancialStates() {
    return financialStates;
  }

  /** Sets the inital order states.
   *
   * @param fulfillmentOrderState The initial fulfillment state.
   *
   * @param financialOrderState The initial financial state.
   */
  public void setInitialOrderStates(final String fulfillmentOrderState,
      final String financialOrderState) {
    log.trace("Entering setInitialOrderStates");
    if ("".equals(financialCurrentState)
        && "".equals(fulfillmentCurrentState)) {
      fulfillmentStates.put(getTime(), fulfillmentOrderState.trim());
      fulfillmentCurrentState = fulfillmentOrderState.trim();
      financialStates.put(getTime(), financialOrderState.trim());
      financialCurrentState = financialOrderState.trim();
    }
    log.trace("Leaving setInitialOrderStates");
  }

  /** Sets the new fulfillment state.
   *
   * @param newState The new fulfillment state.
   */
  public void setFulfillmentStateChanged(final String newState) {
    log.trace("Entering setFulfillmentStateChanged");
    if (!fulfillmentCurrentState.equals(newState.trim())) {
      fulfillmentStates.put(getTime(), newState.trim());
      fulfillmentCurrentState = newState.trim();
      log.debug("new fulfillment state: " + newState);
    }
    log.trace("Leaving setFulfillmentStateChanged");
  }

  /** Sets the new financial state.
   *
   * @param newState The new financial state.
   */
  public void setFinancialStateChanged(final String newState) {
    log.trace("Entering setFinancialStateChanged");
    if (!financialCurrentState.equals(newState.trim())) {
      financialStates.put(getTime(), newState.trim());
      financialCurrentState = newState.trim();
      log.debug("new financial state: " + newState);
    }
    log.trace("Leaving setFinancialStateChanged");
  }

  /** Gets the time.
   *
   * @return Returns the time.
   */
  private long getTime() {
    return new Date().getTime();
  }

  /** String description of the mendoza server status.
   *
   * @return Returns a string description of the status.
   */
  public String toString() {
    StringBuffer sf = new StringBuffer();
    sf.append("------------------------------\n");
    sf.append("M E N D O Z A  -  S T A T U S\n");
    sf.append("------------------------------\n\n");
    sf.append(">> Issued commands: \n\n");
    sf.append(getCommands());
    sf.append("\n\n-------------------------------\n\n");
    sf.append(">> Google order number: ");
    sf.append(getGoogleOrderNumber());
    sf.append("\n\n-------------------------------\n\n");
    sf.append(">> New order notification: \n\n");
    String non = getNewOrderNotificationMsg();
    sf.append(non.replace("><", ">\n<"));
    sf.append("\n\n-------------------------------\n\n");
    sf.append(">> Merchant calculations: \n\n");
    int count = 1;
    for (MendozaMerchantCalculation mc : getMerchantCalculations()) {
      sf.append("\n\n - # ");
      sf.append(count);
      sf.append(" - \n\n");
      sf.append(mc);
      count++;
    }
    sf.append("\n\n-------------------------------\n\n");
    sf.append(">> Risk information: \n\n");
    String risk = getRiskInformationNotificationMsg();
    sf.append(risk.replace("><", ">\n<"));
    sf.append("\n\n-------------------------------\n\n");
    sf.append(">> Financial order states: \n\n");
    sf.append(getFinancialStates().values());
    sf.append("\n\n-------------------------------\n\n");
    sf.append(">> Fulfillment order states: \n\n");
    sf.append(getFulfillmentStates().values());
    return sf.toString();
  }

  /** Clears the status.
   */
  public void reset() {
    resetAll(true);
  }

  /** Clears the state of the messages received.
   */
  public void clearReceivedCheckoutMessages() {
    resetAll(false);
  }

  /** Resets the status.
   *
   * @param resetCommandList True to erase the command log.
   */
  private void resetAll(final boolean resetCommandList) {
    log.trace("Entering resetAll");
    if (resetCommandList) {
      commands = new ArrayList<MendozaCommand>();
    }
    merchantCalculations =
      new ArrayList<MendozaMerchantCalculation>();
    newOrderNotification = null;
    riskInformation = null;
    newOrderNotificationMessage = "";
    riskInformationMessage = "";
    fulfillmentStates = new TreeMap<Long, String>();
    financialStates = new TreeMap<Long, String>();
    fulfillmentCurrentState = "";
    financialCurrentState = "";
    googleOrderNumber = "";
    log.trace("Leaving resetAll");
  }
}
